/**
 * @file ioconfig.h
 * @brief Header file containing macro definitions for I/O pin configuration
 *        capabilitites.
 * @author Vamshi Ponnapalli
 */

#ifndef _IOCONFIG_H
  #define _IOCONFIG_H

  /*****************************************************************************
   * Includes
   ****************************************************************************/
  #include "hal_types.h"
  #include "hal_defs.h"

  #ifdef __cplusplus
    extern "C" {
  #endif
      
  /***************************************************************************
   * Macro definitions
   **************************************************************************/
  // Used as "func" for the macros below
  #define PIN_DEFAULT_TRISTATE   1             
  #define PIN_DEFAULT_PULLUP     2
  #define PIN_DEFAULT_PULLDOWN   3


  //  Pin configuration macros
  #define IO_CONFIG_PERIPHERAL(port, pin)   __IO_CONFIG_PERIPHERAL(port, pin)
  #define IO_CONFIG_INPUT(port, pin, func)  __IO_CONFIG_INPUT(port, pin, func)
  #define IO_CONFIG_OUTPUT(port, pin, val)  __IO_CONFIG_OUTPUT(port, pin, val)
  #define IO_CONFIG_SET(port, pin, val)     __IO_CONFIG_SET(port, pin, val)
  #define IO_CONFIG_SET_HIGH(port, pin)     __IO_CONFIG_SET_HIGH(port, pin)
  #define IO_CONFIG_SET_LOW(port, pin)      __IO_CONFIG_SET_LOW(port, pin)
  #define IO_CONFIG_TGL(port, pin)          __IO_CONFIG_TGL(port, pin)
  #define IO_CONFIG_GET(port, pin)          __IO_CONFIG_GET(port, pin)

  // Internal macros (the macros above need a new round in the preprocessor)
  #define __IO_CONFIG_PERIPHERAL(port, pin)  st( P##port##SEL |= BM(pin); )     
  #define __IO_CONFIG_INPUT(port, pin, func) st( P##port##SEL &= ~BM(pin); \
                                                 P##port##DIR &= ~BM(pin); \
                                                 switch (func) { \
                                                 case PIN_DEFAULT_PULLUP: \
                                                     P##port##INP &= ~BM(pin); \
                                                     P2INP &= ~BM(port + 5); \
                                                     break; \
                                                 case PIN_DEFAULT_PULLDOWN: \
                                                     P##port##INP &= ~BM(pin); \
                                                     P2INP |= BM(port + 5); \
                                                     break; \
                                                 default: \
                                                     P##port##INP |= BM(pin); \
                                                     break; } )
  
  #define __IO_CONFIG_OUTPUT(port, pin, val) st( P##port##SEL &= ~BM(pin); \
                                                 P##port##_##pin## = val; \
                                                 P##port##DIR |= BM(pin); )
  
  #define __IO_CONFIG_SET_HIGH(port, pin)     st( P##port##_##pin## = 1; )
  #define __IO_CONFIG_SET_LOW(port, pin)      st( P##port##_##pin## = 0; )
  
  #define __IO_CONFIG_SET(port, pin, val)    st( P##port##_##pin## = val; )
  #define __IO_CONFIG_TGL(port, pin)         st( P##port##_##pin## ^= 1; )
  #define __IO_CONFIG_GET(port, pin)         (P##port## & BM(pin))
  
  #ifdef __cplusplus
    }
  #endif
#endif